/*
 * Copyright (C), 2005-2019, 深圳市珍爱网信息技术有限公司
 */

package org.idea.ietty.container;

import org.idea.ietty.common.annotation.Resource;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author linhao
 * @date 2020/5/7
 * @Version V1.0
 */
public class BeanFactory {

    private ConcurrentHashMap<String, BeanDefinition> beanDefinitionConcurrentHashMap = new ConcurrentHashMap<>();
    /** 一级缓存 Cache of singleton objects: bean name --> bean instance */
    private final Map<String, Object> singletonObjectMap = new ConcurrentHashMap<String, Object>(256);

    /** 二级缓存 保存所有早期创建的Bean对象，这个Bean还没有完成依赖注入 */
    private final Map<String, Object> earlySingletonObjects = new HashMap<String, Object>(16);

    /** 三级缓存 singletonBean的生产工厂*/
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<String, ObjectFactory<?>>(16);

    /**
     * 根据beanid获取bean
     *
     * @param beanId
     * @return
     */
    public synchronized Object getBean(String beanId) {
        BeanDefinition beanDefinition = beanDefinitionConcurrentHashMap.get(beanId);
        if (beanDefinition == null) {
            throw new RuntimeException("beanDefinition is null , beanId is " + beanId);
        }
        return this.createBean(beanDefinition);
    }


    /**
     * 批量添加bean 用于容器初始化过程中
     *
     * @param beanDefinitionList
     */
    public void addBeanDefinitions(List<BeanDefinition> beanDefinitionList) {
        for (BeanDefinition beanDefinition : beanDefinitionList) {
            beanDefinitionConcurrentHashMap.put(beanDefinition.getId(), beanDefinition);
            if (beanDefinition.isLazyInit() == false && beanDefinition.isSingleton()) {
                System.out.println(" this is lazyInit  obj ");
//                singletonObjectMap.put(beanDefinition.getId(),beanDefinition);
            }
        }
    }

    /**
     * 创建bean
     *
     * @param beanDefinition
     */
    private Object createBean(BeanDefinition beanDefinition) {
        Object object = null;
        try {
            if (beanDefinition.isSingleton()) {
                object = singletonObjectMap.get(beanDefinition.getId());
                if (object != null) {
                    return object;
                } else {
                    object = Class.forName(beanDefinition.getClassName()).newInstance();
                    singletonObjectMap.putIfAbsent(beanDefinition.getId(), object);
                }
            } else {
                object = this.createBeanWithInjectDependency(beanDefinition);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return object;
    }

    /**
     * 创建带有依赖注入的bean
     *
     * @param beanDefinition
     * @return
     */
    private Object createBeanWithInjectDependency(BeanDefinition beanDefinition){
        String className = beanDefinition.getClassName();
        Class<?> aClass = null;
        Object object = null;
        try {
            aClass = Class.forName(className);
            if(beanDefinition.getDefinitionType().equals(BeanDefinition.DefinitionType.XML)){
                //xml定义配置暂时通过类加载的方式来定义
                object = aClass.newInstance();
            }else if(beanDefinition.getDefinitionType().equals(BeanDefinition.DefinitionType.ANNOTATION)){
                object = this.initInjectDependencyBean(beanDefinition);
            }
        } catch (ClassNotFoundException | IllegalAccessException |InstantiationException e) {
            e.printStackTrace();
        }
        return object;
    }


    private Object initInjectDependencyBean(BeanDefinition beanDefinition) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        Object initObject = singletonObjectMap.get(beanDefinition.getId());
        if(initObject!=null){
            return initObject;
        }
        String clazzName = beanDefinition.getClassName();
        Class aClass = Class.forName(clazzName);
        //注解形式的配置暂时通过反射注入的方式实现
        Field[] fields = aClass.getDeclaredFields();
        Object object = aClass.newInstance();
        for (Field field : fields) {
            field.setAccessible(true);
            Annotation[] annotations = field.getAnnotations();
            if(annotations!=null) {
                for (Annotation annotation : annotations) {
                    if(Resource.class.getName().equals(annotation.annotationType().getName())){
                        String beanId = field.getName();
                        Object injectResource = singletonObjectMap.get(beanId);
                        if(injectResource!=null){
                            field.set(object,injectResource);
                        }else{
                            //首先判断是否在二级缓存中存在
                            injectResource = earlySingletonObjects.get(beanId);
                            if(injectResource!=null){
                                field.set(object,injectResource);
                                //先通过引用查找到对应以来，然后放入三级缓存中
                                DependencyObjectProvider dependencyObjectProvider = new DependencyObjectProvider();
                                dependencyObjectProvider.addSingletonFactory(object);
                                singletonFactories.put(beanId,dependencyObjectProvider);
                            }else{
                                earlySingletonObjects.put(beanId,injectResource);
                                BeanDefinition beanDefinitionTemp = beanDefinitionConcurrentHashMap.get(beanId);
                                //通过递归的思路去创建引用
                                injectResource = this.createBeanWithInjectDependency(beanDefinitionTemp);
                                field.set(object,injectResource);
                            }
                        }
                    }
                }
            }
        }
        //整个field已经处理完毕了，因此可以放入一级缓存
        singletonObjectMap.put(beanDefinition.getId(),object);
        return object;
    }




}
